/**
 * cleanObject.js - 深度清理对象工具函数
 *
 * 功能：
 * - 移除对象中的无效值（如 undefined, null, '' 等）
 * - 支持嵌套对象和数组的深度清理
 * - 可自定义排除规则、跳过字段、过滤逻辑
 * - 适用于 API 请求参数处理，避免传递空值或无效字段
 *
 * 使用场景：
 * 在调用接口前对参数进行清洗，确保只传递“有意义”的数据
 */

/**
 * 深度清理对象：移除指定的无效值（如 undefined, null, '' 等）
 * 支持嵌套对象和数组
 *
 * @param {Object|Array} obj - 要清理的对象或数组（必传）
 * @param {Object} options - 配置选项（可选）
 * @param {Array} options.excludeValues - 要排除的值列表，默认：[undefined, null, '']
 * @param {Boolean} options.deep - 是否深度递归处理嵌套结构，默认：true
 * @param {Array<String>} options.skipKeys - 不处理的字段名列表（如密码、临时字段），默认：[]
 * @param {Function} options.filter - 自定义过滤函数，(value, key) => Boolean，返回 false 则排除
 *
 * @returns {Object|Array} 返回清理后的新对象或数组，原对象不变
 */
function cleanObject(obj, options = {}) {
  // 解构配置项，设置默认值
  const {
    // 要排除的值类型（默认排除 undefined、null、空字符串）
    excludeValues = [undefined, null, ''],
    // 是否开启深度清理（处理嵌套对象和数组）
    deep = true,
    // 指定不清理的字段名（例如：即使为空也不删除）
    skipKeys = [],
    // 自定义过滤函数，优先级高于 excludeValues
    filter,
  } = options;

  // 判断是否为纯对象（非 null，且是对象，但不是数组）
  const isObject = (val) => val !== null && typeof val === 'object' && !Array.isArray(val);
  // 判断是否为数组
  const isArray = Array.isArray;

  /**
   * 判断某个值是否应该被排除
   * 规则：
   * 1. 如果字段在 skipKeys 中，不排除
   * 2. 如果提供了 filter 函数，以 filter 返回结果为准
   * 3. 否则检查是否在 excludeValues 中
   */
  const shouldExclude = (value, key) => {
    // 如果该字段被标记为“跳过”，则保留（不删除）
    if (skipKeys.includes(key)) {
      return false;
    }

    // 如果有自定义过滤函数，使用它判断
    if (typeof filter === 'function') {
      return !filter(value, key); // filter 返回 false 表示应排除
    }

    // 否则使用默认规则：值在 excludeValues 中则排除
    return excludeValues.includes(value);
  };

  /**
   * 核心递归函数：深度清理对象或数组
   * @param {Object|Array} data - 当前处理的数据
   * @returns {Object|Array} 清理后的新数据
   */
  function deepClean(data) {
    // 情况1：如果是数组
    if (isArray(data)) {
      return data
        .map((item) => {
          // 如果是对象或数组，并且开启 deep，则递归清理
          if (deep && (isObject(item) || isArray(item))) {
            return deepClean(item);
          }
          return item; // 否则直接返回
        })
        .filter((item) => !shouldExclude(item)); // 过滤掉应排除的项（如 undefined）
    }

    // 情况2：如果是纯对象
    if (isObject(data)) {
      const cleaned = {}; // 创建新对象，避免修改原对象
      for (const [key, value] of Object.entries(data)) {
        // 判断当前值是否应被排除
        if (shouldExclude(value, key)) {
          continue; // 跳过，不加入结果
        }
        // 如果是嵌套对象/数组且开启 deep，则递归清理
        cleaned[key] =
          deep && (isObject(value) || isArray(value)) ? deepClean(value) : value;
      }
      return cleaned;
    }

    // 情况3：基础类型（字符串、数字等），直接返回
    return data;
  }

  // 启动清理流程
  return deepClean(obj);
}

// 导出函数，供其他文件导入使用
export { cleanObject };